package com.ys168.gam.shortestpath.view;

import static com.ys168.gam.shortestpath.uimessage.UIMessageUtil.getString;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;

import com.ys168.gam.shortestpath.algorithm.Dijkstra;
import com.ys168.gam.shortestpath.model.Node;
import com.ys168.gam.shortestpath.persistent.DataExporter;
import com.ys168.gam.shortestpath.persistent.DataImporter;
import com.ys168.gam.shortestpath.uimessage.UIMessageCode;
import com.ys168.gam.shortestpath.uimessage.UIMessageUtil;

/**
 *
 * @author Jason Krebs
 * @date 2015年2月3日
 */
public class MainFrame extends JFrame {

    private static final long serialVersionUID = -4579959636868548754L;
    private static final Logger LOGGER = Logger.getLogger(MainFrame.class.getName());
    private static final URL CRYSTAL_CIRCLE_URL = MainFrame.class.getResource("/image/CrystalCircle.png");

    private CanvaPanel canvaPanel;
    private JToolBar toolBar;
    private JLabel nodeImageLabel;
    private JLabel positionLabel;
    private JPanel contentPane;
    private JPanel controlPanel;
    private JScrollPane scrollPane;
    private JCheckBox isConsecutiveCbx;
    private JButton clearAllButton;
    private JRadioButton addNodeRadio;
    private JRadioButton addLineRadio;
    private JRadioButton normalRadio;
    private JRadioButton moveNodeRadio;
    private JButton resetRunnerButton;
    private JMenuBar menuBar;
    private JMenu mnFile;
    private JMenu mnHelp;
    private JMenuItem mntmAbout;
    private JMenuItem mntmInfomation;
    private JMenuItem mntmExit;
    private JButton runOrStopButton;
    private JCheckBox isShowLineLengthCbx;
    private JCheckBox isDirectedGraphCbx;
    private JMenuItem mntmExport;
    private JMenuItem mntmImport;
    private JButton clearLineButton;
    private JMenu mnLanguage;

    /**
     * Create the frame.
     */
    public MainFrame() {
        setIconImage(Toolkit.getDefaultToolkit().getImage(CRYSTAL_CIRCLE_URL));
        setTitle(getString(UIMessageCode.APPLICATION_NAME));
        setSize(700, 550);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
        setLocation((screen.width - getWidth()) / 2, (screen.height - getHeight()) / 2);

        createMenu();
        createLanguageMenuItem(mnLanguage);
        createContent();

        addListener();
        addMenuListener();
    }

    private void addListener() {
        new DropTarget(canvaPanel, new DropTargetAdapter() {
            @Override
            public void drop(DropTargetDropEvent dropEvent) {
                onDrop(dropEvent);
            }
        });

        canvaPanel.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                if (moveNodeRadio.isSelected()) {
                    canvaPanel.moveNode(e.getPoint());
                }
                if (addLineRadio.isSelected() && canvaPanel.generateLine(e.getPoint())) {
                    resetToNormal();
                }
            }

            @Override
            public void mouseMoved(MouseEvent e) {
                onMouseMoved(e.getPoint());
            }
        });
        canvaPanel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                if (e.getButton() == MouseEvent.BUTTON1) {
                    if (addNodeRadio.isSelected()) {
                        addNode(e.getPoint());
                    }
                    if (normalRadio.isSelected()) {
                        canvaPanel.setSelectedNode(e.getPoint());
                    }
                } else if (e.getButton() == MouseEvent.BUTTON3) {
                    canvaPanel.removeNode(e.getPoint());
                }
            }
        });
        clearAllButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                canvaPanel.clearAllNodes();
            }
        });
        canvaPanel.setOnRunningFinshed(new CanvaPanel.IRunningFinishedListener() {
            @Override
            public void onRunningFinished() {
                setRunStatus(false);
            }
        });

        isShowLineLengthCbx.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                canvaPanel.setShowLineLength(isShowLineLengthCbx.isSelected());
            }
        });
        isDirectedGraphCbx.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                canvaPanel.setDirectedGraph(isDirectedGraphCbx.isSelected());
            }
        });

        resetRunnerButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    Image image = ImageIO.read(MainFrame.class.getResource("/image/Runner.png"));
                    canvaPanel.resetRunner(image);
                } catch (IOException e1) {
                    LOGGER.log(Level.WARNING, e1.getMessage());
                }
            }
        });
        runOrStopButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doRun();
            }
        });
        clearLineButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (!canvaPanel.clearLine()) {
                    JOptionPane.showMessageDialog(MainFrame.this, getString(UIMessageCode.MESSAGE_07));
                }
            }
        });
    }

    private void addMenuListener() {
        mntmExit.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // System.exit(0);
                MainFrame.this.dispose();
            }
        });
        mntmInfomation.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onShowInformation();
            }
        });
        mntmAbout.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(MainFrame.this, getString(UIMessageCode.INFO_02));
            }
        });
        mntmExport.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onExport();
            }
        });
        mntmImport.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onImport();
            }
        });
    }

    private void addNode(Point2D point) {
        Node node = new Node(point, ((ImageIcon) nodeImageLabel.getIcon()).getImage());
        canvaPanel.addNode(node);
        resetToNormal();
    }

    private void createContent() {
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(new BorderLayout(0, 0));

        canvaPanel = new CanvaPanel();
        canvaPanel.setPreferredSize(new Dimension(600, 500));

        canvaPanel.setForeground(Color.WHITE);
        canvaPanel.setBackground(Color.BLACK);
        canvaPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        scrollPane = new JScrollPane(canvaPanel);
        contentPane.add(scrollPane, BorderLayout.CENTER);

        toolBar = new JToolBar();
        toolBar.setFloatable(false);
        contentPane.add(toolBar, BorderLayout.SOUTH);

        positionLabel = new JLabel("x:y:");
        toolBar.add(positionLabel);

        controlPanel = new JPanel();
        contentPane.add(controlPanel, BorderLayout.EAST);
        GridBagLayout gblPanel = new GridBagLayout();
        gblPanel.columnWidths = new int[] { 88 };
        gblPanel.columnWeights = new double[] { 1.0 };
        controlPanel.setLayout(gblPanel);

        isConsecutiveCbx = new JCheckBox(getString(UIMessageCode.CONSECUTIVE), true);
        GridBagConstraints gbcConsecutiveCbx = new GridBagConstraints();
        gbcConsecutiveCbx.fill = GridBagConstraints.HORIZONTAL;
        gbcConsecutiveCbx.insets = new Insets(0, 0, 5, 0);
        gbcConsecutiveCbx.gridx = 0;
        gbcConsecutiveCbx.gridy = 1;
        controlPanel.add(isConsecutiveCbx, gbcConsecutiveCbx);

        isShowLineLengthCbx = new JCheckBox(getString(UIMessageCode.SHOW_LINE_LENGTH), true);
        GridBagConstraints gbcCheckbxShowLineLength = new GridBagConstraints();
        gbcCheckbxShowLineLength.fill = GridBagConstraints.HORIZONTAL;
        gbcCheckbxShowLineLength.insets = new Insets(0, 0, 5, 0);
        gbcCheckbxShowLineLength.gridx = 0;
        gbcCheckbxShowLineLength.gridy = 2;
        controlPanel.add(isShowLineLengthCbx, gbcCheckbxShowLineLength);

        isDirectedGraphCbx = new JCheckBox(getString(UIMessageCode.DIRECTED_GRAPH));
        GridBagConstraints gbcIsDirectedGraphCbx = new GridBagConstraints();
        gbcIsDirectedGraphCbx.fill = GridBagConstraints.HORIZONTAL;
        gbcIsDirectedGraphCbx.insets = new Insets(0, 0, 5, 0);
        gbcIsDirectedGraphCbx.gridx = 0;
        gbcIsDirectedGraphCbx.gridy = 3;
        controlPanel.add(isDirectedGraphCbx, gbcIsDirectedGraphCbx);

        normalRadio = new JRadioButton(getString(UIMessageCode.NORMAL), true);
        GridBagConstraints gbcNormalRadio = new GridBagConstraints();
        gbcNormalRadio.fill = GridBagConstraints.HORIZONTAL;
        gbcNormalRadio.insets = new Insets(0, 0, 5, 0);
        gbcNormalRadio.gridx = 0;
        gbcNormalRadio.gridy = 4;
        controlPanel.add(normalRadio, gbcNormalRadio);

        addNodeRadio = new JRadioButton(getString(UIMessageCode.ADD_NODE));
        GridBagConstraints gbcAddNodeRadio = new GridBagConstraints();
        gbcAddNodeRadio.fill = GridBagConstraints.HORIZONTAL;
        gbcAddNodeRadio.insets = new Insets(0, 0, 5, 0);
        gbcAddNodeRadio.gridx = 0;
        gbcAddNodeRadio.gridy = 5;
        controlPanel.add(addNodeRadio, gbcAddNodeRadio);

        addLineRadio = new JRadioButton(getString(UIMessageCode.ADD_LINE));
        GridBagConstraints gbcAddLineRadio = new GridBagConstraints();
        gbcAddLineRadio.fill = GridBagConstraints.HORIZONTAL;
        gbcAddLineRadio.insets = new Insets(0, 0, 5, 0);
        gbcAddLineRadio.gridx = 0;
        gbcAddLineRadio.gridy = 6;
        controlPanel.add(addLineRadio, gbcAddLineRadio);

        moveNodeRadio = new JRadioButton(getString(UIMessageCode.MOVE_NODE));
        GridBagConstraints gbcMoveNodeRadio = new GridBagConstraints();
        gbcMoveNodeRadio.fill = GridBagConstraints.HORIZONTAL;
        gbcMoveNodeRadio.insets = new Insets(0, 0, 5, 0);
        gbcMoveNodeRadio.gridx = 0;
        gbcMoveNodeRadio.gridy = 7;
        controlPanel.add(moveNodeRadio, gbcMoveNodeRadio);

        ButtonGroup buttonGroup = new ButtonGroup();
        buttonGroup.add(normalRadio);
        buttonGroup.add(addNodeRadio);
        buttonGroup.add(addLineRadio);
        buttonGroup.add(moveNodeRadio);

        resetRunnerButton = new JButton(getString(UIMessageCode.RESET_RUNNER));
        resetRunnerButton.setForeground(Color.BLUE);
        GridBagConstraints gbcShowPlayerToggleButton = new GridBagConstraints();
        gbcShowPlayerToggleButton.fill = GridBagConstraints.HORIZONTAL;
        gbcShowPlayerToggleButton.insets = new Insets(0, 0, 5, 0);
        gbcShowPlayerToggleButton.gridx = 0;
        gbcShowPlayerToggleButton.gridy = 8;
        controlPanel.add(resetRunnerButton, gbcShowPlayerToggleButton);

        runOrStopButton = new JButton(getString(UIMessageCode.RUN));
        runOrStopButton.setForeground(Color.GREEN);
        GridBagConstraints gbcPlayButton = new GridBagConstraints();
        gbcPlayButton.fill = GridBagConstraints.HORIZONTAL;
        gbcPlayButton.insets = new Insets(0, 0, 5, 0);
        gbcPlayButton.gridx = 0;
        gbcPlayButton.gridy = 9;
        controlPanel.add(runOrStopButton, gbcPlayButton);

        clearAllButton = new JButton(getString(UIMessageCode.CLEAR_ALL));
        clearAllButton.setForeground(Color.RED);
        GridBagConstraints gbcClearAllButton = new GridBagConstraints();
        gbcClearAllButton.insets = new Insets(0, 0, 5, 0);
        gbcClearAllButton.fill = GridBagConstraints.HORIZONTAL;
        gbcClearAllButton.anchor = GridBagConstraints.NORTH;
        gbcClearAllButton.gridx = 0;
        gbcClearAllButton.gridy = 10;
        controlPanel.add(clearAllButton, gbcClearAllButton);

        clearLineButton = new JButton(getString(UIMessageCode.CLEAR_LINE));
        clearLineButton.setForeground(Color.ORANGE);
        GridBagConstraints gbcClearLineButton = new GridBagConstraints();
        gbcClearLineButton.fill = GridBagConstraints.HORIZONTAL;
        gbcClearLineButton.insets = new Insets(0, 0, 5, 0);
        gbcClearLineButton.gridx = 0;
        gbcClearLineButton.gridy = 11;
        controlPanel.add(clearLineButton, gbcClearLineButton);

        nodeImageLabel = new JLabel("");
        nodeImageLabel.setIcon(new ImageIcon(CRYSTAL_CIRCLE_URL));
        GridBagConstraints gbcNodeImageLabel = new GridBagConstraints();
        gbcNodeImageLabel.gridx = 0;
        gbcNodeImageLabel.gridy = 12;
        controlPanel.add(nodeImageLabel, gbcNodeImageLabel);
    }

    /**
     * 创建语言菜单
     *
     * @param menu
     */
    private void createLanguageMenuItem(JMenu menu) {
        Map<String, String> allLanguages = UIMessageUtil.loadAllLanguage();
        ButtonGroup buttonGroup = new ButtonGroup();
        for (Map.Entry<String, String> entry : allLanguages.entrySet()) {
            final JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(entry.getValue());
            menuItem.setActionCommand(entry.getKey());
            menu.add(menuItem);
            buttonGroup.add(menuItem);
            menuItem.setSelected(entry.getKey().equalsIgnoreCase(UIMessageUtil.getLanguage()));

            menuItem.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    UIMessageUtil.loadMessage(menuItem.getActionCommand());
                    updateUIMessage();
                }
            });
        }
    }

    private void createMenu() {
        menuBar = new JMenuBar();
        setJMenuBar(menuBar);

        mnFile = new JMenu(getString(UIMessageCode.FILE));
        menuBar.add(mnFile);

        mntmImport = new JMenuItem(getString(UIMessageCode.IMPORT));
        mntmImport.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.CTRL_MASK));
        mnFile.add(mntmImport);

        mntmExport = new JMenuItem(getString(UIMessageCode.EXPORT));
        mntmExport.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
        mnFile.add(mntmExport);

        mntmExit = new JMenuItem(getString(UIMessageCode.EXIT));
        mntmExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.ALT_MASK));
        mnFile.add(mntmExit);

        mnHelp = new JMenu(getString(UIMessageCode.HELP));
        menuBar.add(mnHelp);

        mntmInfomation = new JMenuItem(getString(UIMessageCode.INFORMATION));
        mnHelp.add(mntmInfomation);

        mntmAbout = new JMenuItem(getString(UIMessageCode.ABOUT));
        mnHelp.add(mntmAbout);

        mnLanguage = new JMenu(getString(UIMessageCode.LANGUAGE));
        menuBar.add(mnLanguage);
    }

    private void doRun() {
        boolean isRunnable = canvaPanel.isRunnable();
        nodeImageLabel.setEnabled(isRunnable);
        setRunStatus(isRunnable);
        if (isRunnable) {
            Node source = canvaPanel.getSource();
            Node target = canvaPanel.getSelectedNode();
            Dijkstra dijkstra = new Dijkstra(isDirectedGraphCbx.isSelected());
            dijkstra.execute(source);
            canvaPanel.run(dijkstra.getPath(target));
        } else {
            canvaPanel.stop();
        }
    }

    private void onDrop(DropTargetDropEvent dropEvent) {
        try {
            if (!dropEvent.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
                dropEvent.rejectDrop();
                return;
            }

            dropEvent.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
            List<?> list = (List<?>) (dropEvent.getTransferable().getTransferData(DataFlavor.javaFileListFlavor));
            Iterator<?> iterator = list.iterator();
            while (iterator.hasNext()) {
                File f = (File) iterator.next();
                String path = f.getAbsolutePath().toLowerCase();
                if (path.endsWith(".json")) {
                    List<Node> nodes = DataImporter.importFrom(path, ((ImageIcon) nodeImageLabel.getIcon()).getImage());
                    canvaPanel.loadNodes(nodes);
                    JOptionPane.showMessageDialog(this,
                            String.format(getString(UIMessageCode.MESSAGE_01), nodes.size()));
                }
            }
            dropEvent.dropComplete(true);
        } catch (Exception e) {
            LOGGER.log(Level.WARNING, e.getMessage());
        }
    }

    private void onExport() {
        if (canvaPanel.getAllNodes().isEmpty()) {
            JOptionPane.showMessageDialog(this, getString(UIMessageCode.MESSAGE_02));
            return;
        }
        DataExporter.doExport(canvaPanel.getAllNodes());
    }

    private void onImport() {
        JFileChooser chooser = new JFileChooser();
        chooser.setDialogTitle(getString(UIMessageCode.MESSAGE_05));
        chooser.setDialogType(JFileChooser.OPEN_DIALOG);
        chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        chooser.setFileFilter(new FileNameExtensionFilter("*.json", "json"));
        if (chooser.showOpenDialog(MainFrame.this) == JFileChooser.APPROVE_OPTION) {
            File selected = chooser.getSelectedFile();
            if (selected != null) {
                String path = selected.getPath();
                List<Node> nodes = DataImporter.importFrom(path, ((ImageIcon) nodeImageLabel.getIcon()).getImage());
                canvaPanel.loadNodes(nodes);
                JOptionPane.showMessageDialog(this, String.format(getString(UIMessageCode.MESSAGE_01), nodes.size()));
            }
        }
    }

    private void onMouseMoved(Point point) {
        Node focused = canvaPanel.getFocusedNode();
        double fx = focused == null ? 0 : focused.getLocation().getX();
        double fy = focused == null ? 0 : focused.getLocation().getY();
        positionLabel.setText(String.format("X=%d,Y=%d,N=%d,L=%d,FX=%.2f,FY=%.2f", point.x, point.y,
                canvaPanel.getNodeCount(), canvaPanel.getLineCount(), fx, fy));
    }

    private void onShowInformation() {
        InformationDialog dialog = new InformationDialog();
        dialog.setVisible(true);
    }

    private void resetToNormal() {
        if (!isConsecutiveCbx.isSelected()) {
            normalRadio.setSelected(true);
        }
    }

    private void setRunStatus(boolean isReadyToRun) {
        boolean enabled = !isReadyToRun;
        clearAllButton.setEnabled(enabled);
        clearLineButton.setEnabled(enabled);
        addNodeRadio.setEnabled(enabled);
        addLineRadio.setEnabled(enabled);
        normalRadio.setEnabled(enabled);
        moveNodeRadio.setEnabled(enabled);
        resetRunnerButton.setEnabled(enabled);
        isDirectedGraphCbx.setEnabled(enabled);
        isConsecutiveCbx.setEnabled(enabled);
        isShowLineLengthCbx.setEnabled(enabled);

        String text = isReadyToRun ? UIMessageCode.STOP : UIMessageCode.RUN;
        runOrStopButton.setText(getString(text));
    }

    private void updateUIMessage() {
        setTitle(getString(UIMessageCode.APPLICATION_NAME));

        isConsecutiveCbx.setText(getString(UIMessageCode.CONSECUTIVE));
        isShowLineLengthCbx.setText(getString(UIMessageCode.SHOW_LINE_LENGTH));
        isDirectedGraphCbx.setText(getString(UIMessageCode.DIRECTED_GRAPH));

        normalRadio.setText(getString(UIMessageCode.NORMAL));
        addNodeRadio.setText(getString(UIMessageCode.ADD_NODE));
        addLineRadio.setText(getString(UIMessageCode.ADD_LINE));
        moveNodeRadio.setText(getString(UIMessageCode.MOVE_NODE));

        resetRunnerButton.setText(getString(UIMessageCode.RESET_RUNNER));
        runOrStopButton.setText(getString(UIMessageCode.RUN));
        clearAllButton.setText(getString(UIMessageCode.CLEAR_ALL));
        clearLineButton.setText(getString(UIMessageCode.CLEAR_LINE));

        mnFile.setText(getString(UIMessageCode.FILE));
        mnHelp.setText(getString(UIMessageCode.HELP));
        mnLanguage.setText(getString(UIMessageCode.LANGUAGE));
        mntmImport.setText(getString(UIMessageCode.IMPORT));
        mntmExport.setText(getString(UIMessageCode.EXPORT));
        mntmExit.setText(getString(UIMessageCode.EXIT));
        mntmAbout.setText(getString(UIMessageCode.ABOUT));
        mntmInfomation.setText(getString(UIMessageCode.INFORMATION));
    }

}
